/*
 * Copyright (C)\n , 2015-2019
 * FileName: ClassFile
 * Author:   zhao
 * Date:     2019/2/2 20:46
 * Description: 类文件
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.jjvm.classfile;

import com.lizhaoblog.jjvm.common.CommonValue;
import com.lizhaoblog.jjvm.classfile.attribute.AttributeInfo;
import com.lizhaoblog.jjvm.classfile.attribute.BaseAttributeInfo;
import com.lizhaoblog.jjvm.classfile.constantpool.ConstantPool;

import java.util.Arrays;

/**
 * 〈一句话功能简述〉<br>
 * 〈类文件〉
 *
 * @author zhao
 * @date 2019/2/2 20:46
 * @since 1.0.0
 */
public class ClassFile {
    /**
     * 对应的直接数组
     */
    private byte[] bytes;
    /**
     * 对应的直接字节流读取类
     */
    private ClassReader reader;

    // 下面的都是类文件必须有的内容，而且严格按照这个顺序

    private int magic;
    private int minorVersion;
    private int majorVersion;
    //    private int constantPoolCount;
    private ConstantPool constantPool;
    private int accessFlag;
    private int classNameIndex;
    private int superClassNameIndex;
    //    private int interfaceCount;
    private int[] interfaceIndexes;
    //    private int fieldCount;
    private MemberInfo[] fieldInfos;
    //    private int methodCount;
    private MemberInfo[] methodInfos;
    //    private int attributeCount;
    private AttributeInfo[] attributeInfos;

    public ClassFile(byte[] bytes) {
        this.bytes = bytes;
        this.reader = new ClassReader(bytes);

        readMagic();
        readVersion();

        readConstantPool();
        readAccessFlag();
        readClassNameIndex();
        readSuperClassNameIndex();
        readInterfaceIndexes();
        readFields();
        readMethods();
        readAttributes();
    }

    private void readMagic() {
        String magic = reader.nextU4ToHexString();
        if (!"cafebabe".equals(magic)) {
            throw new ClassFormatError("magic!");
        }
    }

    private void readVersion() {
        this.minorVersion = this.reader.nextU2ToInt();
        this.majorVersion = this.reader.nextU2ToInt();
        // jdk11对应的major为55，支持jdk1.2-jdk11
        if (this.majorVersion >= CommonValue.JDK_VERSION_MAJOR_02
                && this.majorVersion <= CommonValue.JDK_VERSION_MAJOR_11
                && this.minorVersion == CommonValue.JDK_VERSION_MINOR_00) {
            return;
        }
        throw new UnsupportedClassVersionError();
    }

    private void readConstantPool() {
        this.constantPool = new ConstantPool(reader);
    }

    private void readAccessFlag() {
        this.accessFlag = this.reader.nextU2ToInt();
    }

    private void readClassNameIndex() {
        classNameIndex = reader.nextU2ToInt();
    }

    private void readSuperClassNameIndex() {
        superClassNameIndex = reader.nextU2ToInt();
    }

    private void readInterfaceIndexes() {
        interfaceIndexes = reader.nextUint16s();
    }

    private void readFields() {
        fieldInfos = MemberInfo.readMembers(constantPool, reader);
    }

    private void readMethods() {
        methodInfos = MemberInfo.readMembers(constantPool, reader);
    }

    private void readAttributes() {
        attributeInfos = BaseAttributeInfo.readAttributes(reader, constantPool);
    }

    public int getMinorVersion() {
        return minorVersion;
    }

    public void setMinorVersion(int minorVersion) {
        this.minorVersion = minorVersion;
    }

    public int getMajorVersion() {
        return majorVersion;
    }

    public void setMajorVersion(int majorVersion) {
        this.majorVersion = majorVersion;
    }

    public ConstantPool getConstantPool() {
        return constantPool;
    }

    public void setConstantPool(ConstantPool constantPool) {
        this.constantPool = constantPool;
    }

    public int getAccessFlag() {
        return accessFlag;
    }

    public void setAccessFlag(int accessFlag) {
        this.accessFlag = accessFlag;
    }

    @Override
    public String toString() {
        return "ClassFile{" +
                "bytes=" + Arrays.toString(bytes) +
                "\n , reader=" + reader +
                "\n , magic=" + magic +
                "\n , minorVersion=" + minorVersion +
                "\n , majorVersion=" + majorVersion +
                "\n , constantPool=" + constantPool +
                "\n , accessFlag=" + accessFlag +
                "\n , classNameIndex=" + classNameIndex +
                "\n , superClassNameIndex=" + superClassNameIndex +
                "\n , interfaceIndexes=" + Arrays.toString(interfaceIndexes) +
                "\n , fieldInfos=" + Arrays.toString(fieldInfos) +
                "\n , methodInfos=" + Arrays.toString(methodInfos) +
                "\n , attributeInfos=" + Arrays.toString(attributeInfos) +
                '}';
    }
}